using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using XmlObjects.Parsers;

namespace XmlObjects
{
	public class Hydrator<T> : Parser<T> where T : new()
	{
		private Type theType;
		private readonly Dictionary<PropertyInfo, IXmlParser> parsers = new Dictionary<PropertyInfo, IXmlParser>();

		public Hydrator()
		{
			theType = typeof (T);
			foreach (PropertyInfo info in theType.GetProperties())
			{
				if (Attribute.IsDefined(info, typeof(PropertyAttribute)))
				{
					parsers.Add(info, GetParser(info));
				}
			}
		}

		/// <summary>
		///   I expect the parser to know exactly how to set my property 
		///     given a select statement, an xml node and an object type.
		/// </summary>
		/// <param name="node"></param>
		/// <returns></returns>
		public override T GetValue(XmlNode node)
		{
			XmlNode localNode = node.SelectSingleNode(SelectString);
			return GetValueLocal(localNode);
		}

		public override T GetValueLocal(XmlNode node)
		{
			T theObject = GetObject(node);
			foreach (KeyValuePair<PropertyInfo, IXmlParser> pair in parsers)
			{
				//  I give this node to the parser.
				//     If it's a simple parser it will select itself out of the node.
				//		If it's a collection parser it will get a list of nodes.

				pair.Key.SetValue(theObject, pair.Value.GetValue(node), null);
			}
			return theObject;
		}



		private T GetObject(XmlNode node)
		{
			if (!HasSubTypes())
			{
				return new T();
			}
			else
			{
				Type subType = null;
				if (FindSubtypeBy.Equals("#name"))
					subType = Map.SubTypeMap[node.Name];
				else
				{
					XmlNode idNode = node.SelectSingleNode(FindSubtypeBy);
					if (idNode == null || string.IsNullOrEmpty(node.InnerXml))
					{
						subType = defaultSubType;
					}
					else
					{
						subType = Map.SubTypeMap[idNode.InnerXml];
					}
				}
				if (subType == null)
					throw new ParserException(string.Format("Unable to determine subtype of node: {0}.", node.Name));

				return (T) Activator.CreateInstance(subType);
			}
		}
	}
}